﻿using System;
using System.Linq;
using System.Numerics;
using System.Diagnostics;
using Windows.Foundation;
using Windows.Storage;
//using Windows.Storage.Streams;
using Windows.UI.Core;
using Windows.UI;
using Microsoft.Graphics.Canvas;
using Microsoft.Graphics.Canvas.Effects;
using Microsoft.Graphics.Canvas.UI;
using Microsoft.Graphics.Canvas.UI.Xaml;
using System.Threading.Tasks;
using System.Collections.Generic;
using Svga.SvgaPlayer.Models;
using Windows.UI.Popups;
//using Windows.Storage.Pickers;
// using ImageMagick;
using System.Runtime.InteropServices;
using System.IO;
using Windows.UI.Xaml.Media.Imaging;
using Windows.Graphics.DirectX;

namespace Svga.SvgaPlayer.Controls {
  public partial class SvgaPlayer {
    //const string DllName = "pic2video";
    //[DllImport(DllName)]
    //public static extern bool DoConvert([MarshalAs(UnmanagedType.LPStr)] string input, [MarshalAs(UnmanagedType.LPStr)] string output, int fps, [MarshalAs(UnmanagedType.LPStr)] string gpu);
    /// <summary>
    /// 当前已播放次数.
    /// </summary>
    private int _playedCount;
    /// <summary>
    /// 舞台是否已经初始化.
    /// </summary>
    private bool _isStageInited;

    /// <summary>
    /// 舞台资源是否准备完毕.
    /// </summary>
    private bool _isResourceReady;

    /// <summary>
    /// 舞台资源对象.
    /// </summary>
    private StageResource _stageResource;
    /// <summary>
    /// 帧率.
    /// </summary>
    private int _rate = 25;
    private float _dpi;
    private CanvasDevice _device;
    private int _width;
    private int _height;
    private delegate void OnCompleted();
    /// <summary>
    /// 本地缓存目录.
    /// </summary>
    private StorageFolder _localFolder
    {
      get => ApplicationData.Current.LocalFolder;
    }
    /// <summary>
    /// 初始化舞台资源.
    /// </summary>
    private async void InitStageResource (int fps) {
      if (this._isResourceReady) {
        return;
      }

      if (this._stageResource == null) {
        this._stageResource = new StageResource(this.Stage);
      }

      var sprites = this._sprites;
      foreach (var sprite in sprites) {
        var imageKey = sprite.ImageKey;
        var image = this._images.FirstOrDefault(item => item.Key == imageKey);

        // 有可能导出的 SVGA Image 实际不存在 PNG Binary.
        if (image.Value != null) {
          await this._stageResource.AddSprite(sprite, image.Value);
        }
      }
      _rate = fps;
      _width = (int)StageWidth;
      _height = (int)StageHeight;
      this._isResourceReady = true;
    }

    /// <summary>
    /// 绘制单个 Sprite.
    /// </summary>
    /// <param name="frame"></param>
    /// <param name="session"></param>
    /// <param name="sprite"></param>
    private void DrawSingleSprite (int frame, CanvasDrawingSession session, SvgaSprite sprite) {
      var currentFrame = sprite.Frames[frame];
      if (currentFrame == null) {
        return;
      }

      var width = 0f;
      var height = 0f;
      var alpha = currentFrame.Alpha;
      var layout = currentFrame.Layout;
      if (layout != null) {
        width = layout.Width;
        height = layout.Height;
      }

      var transform = currentFrame.Transform;
      if (transform != null) {
        // Sprite 透视参数.
        var perspective = new Matrix4x4(new Matrix3x2(
          transform.A, transform.B, transform.C, transform.D, transform.Tx, transform.Ty
        ));

        session.DrawImage(
          sprite.CanvasBitmap, 0, 0, new Rect(0, 0, width, height), alpha,
          CanvasImageInterpolation.Linear, perspective
        );
      } else {
        session.DrawImage(
          sprite.CanvasBitmap, 0, 0, new Rect(0, 0, width, height), alpha,
          CanvasImageInterpolation.Linear
        );
      }
    }
    /// <summary>
    /// 合成视频
    /// </summary>
    /// <param name="picsPath">形如C:\export_%d.png</param>
    /// <returns></returns>
    private void ConvertVideo(string picsPath)//async 
    {
      using (var pro = new Process())
      {
        //Debug.WriteLine($"帧率：===={_rate}");
        //var outFolder = KnownFolders.PicturesLibrary.Path;
        //cpu
        var arg = $"-r {_rate} -i {picsPath}\\%d.png -tiles 24 -c:v libx264 -vf scale=iw:-2 -pix_fmt yuv420p -b:v 5000k -y {picsPath}\\output.mp4";
        if (GpuText == "NVIDIA"){
          //n卡加速
          arg = $"-hwaccel cuvid -hwaccel_output_format cuda -r {_rate} -i {picsPath}\\%d.png -c:v h264_nvenc -vf scale=iw:-2 -b:v 5000k -pix_fmt yuv420p -y {picsPath}\\output.mp4";
        }
        if(GpuText == "AMD"){
          //a卡加速
          arg = $"-r {_rate} -i {picsPath}\\%d.png -vcodec h264_amf -vf scale=iw:-2 -pix_fmt yuv420p -b:v 5000k -y {picsPath}\\output.mp4";
        }
        //Debug.WriteLine(arg);
        pro.StartInfo.UseShellExecute = false;
        pro.StartInfo.ErrorDialog = false;
        //pro.StartInfo.RedirectStandardOutput = true;
        pro.StartInfo.RedirectStandardError = true;
        pro.StartInfo.CreateNoWindow = true;
        pro.StartInfo.FileName = $"{picsPath}\\ffmpeg";//ffmpegPath;
        pro.StartInfo.Arguments = arg;
        pro.Start();
      }/**/
    }
    /// <summary>
    /// 清除缓存图片
    /// </summary>
    /// <param name="localFolder"></param>
    private async void ClearImageFile()
    {
      var items = await _localFolder.GetFilesAsync();
      foreach (var item in items)
      {
        if (item.Name.EndsWith(".png"))
        {
          await item.DeleteAsync();
        }
      }
    }
    /// <summary>
    /// 从资源目录拷贝文件到本地文件夹
    /// </summary>
    private async Task CopyFileFromAssetsToLocalStorageAsync()
    {
      try
      {
        var fileName = "ffmpeg.exe";
        // 获取本地文件夹
        //StorageFolder localFolder = ApplicationData.Current.LocalFolder;
        StorageFile file = await _localFolder.TryGetItemAsync(fileName) as StorageFile;
        if(file == null)
        {
          // 获取Assets文件夹中的文件
          var storageFile = await StorageFile.GetFileFromApplicationUriAsync(new Uri($"ms-appx:///Assets/{fileName}"));

          // 复制文件到本地文件夹
          await storageFile.MoveAsync(_localFolder, fileName, NameCollisionOption.ReplaceExisting);
        }
      }
      catch (Exception ex)
      {
        Debug.WriteLine($"复制错误：\n{ex.Message}");
      }
    }
    private async void SaveOffscreenRenderToImage(List<SvgaSprite> sprites, int frame, OnCompleted callback)
    {
      var _out_width = _width * 2;
      await Task.Run(async () =>
      {
        var renderTarget = new CanvasRenderTarget(_device, _width, _height, _dpi);
        var outputTarget = new CanvasRenderTarget(_device, _out_width, _height, _dpi);
        using (var ds = renderTarget.CreateDrawingSession())
        {
          // 在画布上绘制一些内容
          ds.Clear(Colors.Transparent);
          // 遍历 Sprites 进行绘制.
          foreach (var sprite in sprites)
          {
            this.DrawSingleSprite(frame, ds, sprite);
          }
        }
        var outputPng = $"{frame}.png";
        
        var folder = _localFolder.Path;
        try
        {
          using (var oDs = outputTarget.CreateDrawingSession())
          {
            // 填充黑色背景
            oDs.Clear(Colors.Black);
            // 创建 ColorMatrixEffect
            var colorMatrixEffect = new ColorMatrixEffect
            {
              Source = renderTarget,
              ColorMatrix = new Matrix5x4
              {
                M11 = 0,M12 = 0,M13 = 0,M14 = 0, // Red channel = 0
                M21 = 0,M22 = 0,M23 = 0,M24 = 0, // Green channel = 0
                M31 = 0,M32 = 0,M33 = 0,M34 = 0, // Blue channel = 0
                M41 = 0,M42 = 0,M43 = 0,M44 = 1, // Alpha channel = alpha
                M51 = 1,M52 = 1,M53 = 1,M54 = 0, // Output color = 1 (white)
              }
            };
            oDs.DrawImage(colorMatrixEffect);
            oDs.DrawImage(renderTarget, _width, 0);
          }
          //var localFolder = ApplicationData.Current.LocalFolder;KnownFolders.PicturesLibrary
          var outputFile = await _localFolder.CreateFileAsync(outputPng, CreationCollisionOption.ReplaceExisting);//.SavedPictures // 将渲染结果保存为PNG图片
          using (var stream = await outputFile.OpenAsync(FileAccessMode.ReadWrite))
          {
            await outputTarget.SaveAsync(stream, CanvasBitmapFileFormat.Png);
          }
          if (frame == TotalFrame - 1)
          {
            ConvertVideo(folder);
            //var filePath = Path.Combine(folder, "%d.png");
            //var mediaPath = Path.Combine(folder, "output.mp4");
            //bool flag = DoConvert(filePath, mediaPath, _rate, "NVIDIA");
            //if (flag)
            //{
              callback();
            //}
          }
        }
        catch (Exception ex)
        {
          Debug.WriteLine($"生成错误：\n{ex.Message}");
        }
      });
    }
    /// <summary>
    /// Stage OnCreateResource 事件.
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="args"></param>
    private async void StageOnCreateResources (CanvasAnimatedControl sender, CanvasCreateResourcesEventArgs args) {
      // ...
      //args.TrackAsyncAction(CopyFileFromAssetsToLocalStorageAsync().AsAsyncAction());
      await CopyFileFromAssetsToLocalStorageAsync();
      _device = sender.Device;
      _dpi = sender.Dpi;
    }

    /// <summary>
    /// Stage OnUpdate 事件.
    /// 用于更新舞台数据, 一般在运行一次 Update 后运行一次 Draw 事件.
    /// 但当程序运行缓慢时, 可能会运行多次 Update 后再执行一次 Draw 事件.
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="args"></param>
    private void StageOnUpdate (ICanvasAnimatedControl sender, CanvasAnimatedUpdateEventArgs args) {
      // ...
    }
    /// <summary>
    /// Stage OnDraw 事件.
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="args"></param>
    private void StageOnDraw (ICanvasAnimatedControl sender, CanvasAnimatedDrawEventArgs args) {
      if (!this._isResourceReady || !this._isStageInited) {
        return;
      }

      var drawFrame = this._drawNextFrame > -1
        ? this._drawNextFrame
        : this.CurrentFrame;
      var stageResource = this._stageResource;
      var sprites = stageResource.Sprites;
      if (LoopCount == 0)//
      {
        using (var session = args.DrawingSession)
        {
          // 遍历 Sprites 进行绘制.
          foreach (var sprite in sprites)
          {
            this.DrawSingleSprite(drawFrame, session, sprite);
          }
        }
      }
      this.CurrentFrame = drawFrame;
      this._drawNextFrame = -1;

      if (this.IsInPlay) {
        var nextFrame = drawFrame + 1;
        var isLoopFinished = nextFrame > this.TotalFrame - 1;
        if (isLoopFinished) {
          nextFrame = 0;
          this._playedCount++;
        }
        this.CurrentFrame = nextFrame;
        if (LoopCount > 0)
        {
          SaveOffscreenRenderToImage(sprites, drawFrame,async ()=>
          {
            ClearImageFile();
            await Dispatcher.RunAsync(CoreDispatcherPriority.Low, async () =>
            {
              var dialog = new MessageDialog("生成成功！");
              await dialog.ShowAsync();
            });
          });
        }
      }
      // 判断是否继续播放.
      // 此条件需要写在结尾, 否则当前帧会被清空而显示空白.
      if (this.LoopCount > 0 && this._playedCount >= this.LoopCount) {
        this.Pause();
        this.NotifyLoopFinish();
      }

      this.OnLoop?.Invoke();
    }

    /// <summary>
    /// 通知 UI 线程播放完成.
    /// </summary>
    private async void NotifyLoopFinish () {
      await this.Dispatcher.RunAsync(CoreDispatcherPriority.Low, () => {
        this.OnLoopFinish?.Invoke();
      });
    }
  }
}
